package per.hnvcam.util.console.process;

import java.io.PrintStream;
import java.util.concurrent.TimeoutException;

import per.hnvcam.util.console.ClientSupport;
import per.hnvcam.util.console.ConsoleInputStream;
import per.hnvcam.util.console.ConsoleOutputStream;
import per.hnvcam.util.console.Constants;
import per.hnvcam.util.console.ssh.SSHClientSupport;

public class ExpectAndSend implements Runnable {

   public enum ClientType {
      CMD_CLIENT, SSH_CLIENT
   };

   private ConsoleOutputStream outputStream;
   private ConsoleInputStream inputStream;
   private ConsoleOutputStream errorStream;
   private StringBuilder buffer;

   private PrintStream outPrinter;
   private PrintStream errPrinter;
   private Thread printError;
   private ClientSupport client;
   private ClientType clientType;

   public ExpectAndSend(ClientSupport client) {
      if (!client.isOpened()) {
         throw new IllegalStateException("Client must be connected to host.");
      }
      this.client = client;

      if (client instanceof SSHClientSupport) {
         clientType = ClientType.SSH_CLIENT;
      } else {
         clientType = ClientType.CMD_CLIENT;
      }

      this.outputStream = client.getOutputStream();
      this.inputStream = client.getInputStream();
      this.errorStream = client.getErrorStream();
      this.outPrinter = System.out;
      this.errPrinter = System.err;
      this.buffer = new StringBuilder();
      printError = new Thread(this);
      printError.setDaemon(true);
      printError.start();
   }

   public void setOutPrinter(PrintStream printer) {
      this.outPrinter = printer;
   }

   public void setErrPrinter(PrintStream printer) {
      this.errPrinter = printer;
   }

   public void expect(String regrex) throws TimeoutException {
      this.expect(regrex, Constants.DEFAULT_TIMEOUT);
   }

   public boolean reExpect(String regrex) {
      return buffer.toString().matches(regrex);
   }

   public void expect(String regrex, long timeout) throws TimeoutException {
      long endTime = System.currentTimeMillis() + timeout;
      while (System.currentTimeMillis() < endTime) {
         if (outputStream.isAvailable()) {
            while (outputStream.isAvailable()) {
               String line = outputStream.readLine();
               outPrinter.println(line);
               buffer.append(line);
            }
         }
         if (buffer.toString().matches(regrex)) {
            flushBuffer();
            return;
         }
         try {
            Thread.sleep(500);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      throw new TimeoutException("Unable to receive expected message " + regrex);
   }

   public void send(String command) {
      char lastChar = command.charAt(command.length() - 1);
      if (lastChar != Constants.CARRIAGE_RETURN || lastChar != Constants.LINE_FEED) {
         command = command.concat(String.valueOf(Constants.CARRIAGE_RETURN) + String.valueOf(Constants.LINE_FEED));
      }
      inputStream.add(command);
   }

   public void expectAndSend(String regrex, String command, long timeout) throws TimeoutException {
      expect(regrex, timeout);
      send(command);
   }

   public void expectAndSend(String regrex, String command) throws TimeoutException {
      expectAndSend(regrex, command, Constants.DEFAULT_TIMEOUT);
   }

   public void flushBuffer() {
      buffer.setLength(0);
   }

   public void run() {
      while (client.isOpened()) {
         if (errorStream.isAvailable()) {
            errPrinter.println(errorStream.readLine());
         }
         try {
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }

   public ClientType getClientType() {
      return clientType;
   }
}
